/**
 * 
 */
package org.metaverse.h2.sql;

import org.metaverse.h2.cache.ValueCache;
import org.metaverse.warehouse.Senary;
import org.metaverse.warehouse.Senary.Slot;
import org.metaverse.warehouse.com.Conjunction;
import org.metaverse.warehouse.com.Disjunction;
import org.metaverse.warehouse.com.Equals;
import org.metaverse.warehouse.com.Greater;
import org.metaverse.warehouse.com.Junction;
import org.metaverse.warehouse.com.Less;
import org.metaverse.warehouse.com.Like;
import org.metaverse.warehouse.com.NotEquals;
import org.metaverse.warehouse.com.Predicate;
import org.metaverse.warehouse.com.ValueConstraint;
import org.metaverse.warehouse.com.Variable;

public class ValueConstraint2SQL implements ValueConstraint.Visitor
{
	

	private StringBuilder _sb;
	private Senary.Slot _slot;
	private boolean _requireNode = false;
	private ValueCache _nodeCache;

	private void onJ(Junction aJunct, String aRep) {
		_sb.append('(');
		final ValueConstraint[] vcs = aJunct.getValueConstraints();
		for(int i=0;i<vcs.length;i++)
		{
			if(i>0)
				_sb.append(aRep);
			vcs[i].visit(this);
		}
		_sb.append(')');
	}
	
	private void onP(Predicate aPred, String aRep) {
		_sb.append(slotToColumnName());
		_sb.append(aRep);
		if(_requireNode)
		{
			_sb.append("'");
			_sb.append(aPred.getValue().stringValue());
			_sb.append("'");
		}
		else
			_sb.append(_nodeCache.getIdForValue(aPred.getValue(),false));
	}
	
	private String slotToColumnName() {
		switch(_slot)
		{
		case Subject: return _requireNode? "SNODE.VAL":"SID";
		case Predicate: return _requireNode? "PNODE.VAL":"PID";
		case Object: return _requireNode? "ONODE.VAL":"OID";
		case Context: return _requireNode? "CNODE.VAL":"CID";
		case User: return _requireNode? "UNODE.VAL":"UID";
		case LastAssertTime: return "LAT";
		default:
			throw new IllegalArgumentException(String.format("Unexpected slot '%s'.", _slot));
		}
	}
	
	@Override
	public void on(Conjunction aAnd) {
		onJ(aAnd," AND ");
	}

	@Override
	public void on(Disjunction aOr) {
		onJ(aOr," OR ");
	}

	@Override
	public void on(Equals aEq) {
		onP(aEq, " = ");
	}

	@Override
	public void on(Greater aGr) {
		requereNode();
		onP(aGr, " > ");
	}

	private void requereNode() {
		if(_slot != Slot.LastAssertTime)
			_requireNode = true;
	}

	@Override
	public void on(Less aLs) {
		requereNode();
		onP(aLs, " < ");
	}

	@Override
	public void on(Like aLk) {
		requereNode();
		onP(aLk, " LIKE ");
	}

	@Override
	public void on(NotEquals aNe) {
		onP(aNe, " <> ");
	}

	@Override
	public void on(Variable aVar) {
		throw new UnsupportedOperationException();
	}
	
	public void compose(
			Senary.Slot aSlot,
			ValueConstraint aVConstr, 
			StringBuilder aSb,
			ValueCache aNodeCache)
	{
		_nodeCache = aNodeCache;
		_slot = aSlot;
		_sb = aSb;
		_requireNode = false;
		aVConstr.visit(this);
	}

	/**
	 * @return the requireNode
	 */
	public boolean isRequireNode() {
		return _requireNode;
	}
}